package com.buffet.spring;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class AnnotationApplicationContext {
    private Class configClass;
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

    public AnnotationApplicationContext(Class configClass) {
        this.configClass = configClass;
        // component scan
        componentScan(this.configClass);
        // bean definition
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.isLazy()) {
                singletonObjects.put(beanName, createBean(beanName, beanDefinition));
            }
        }
    }

    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        try {
            Class beanClass = beanDefinition.getBeanClass();
            Object bean = beanClass.newInstance();
            for (Field field : beanClass.getFields()) {
                if (field.isAnnotationPresent(Autowird.class)) {
                    field.setAccessible(true);
                    Object fieldValue = getBean(beanName);
                    field.set(bean, fieldValue);
                }
            }
            return bean;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private void componentScan(Class configClass) {
        // 扫描类
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String path = componentScan.value().replace(".", "/");
            System.out.println(path);
            ClassLoader classLoader = AnnotationApplicationContext.class.getClassLoader();
            try {
                URL resource = classLoader.getResource(path);
                File file = new File(resource.getFile());
                List<File> classFile = new ArrayList<>();
                for (File f : file.listFiles()) {
                    if (f.isFile()) {
                        classFile.add(f);
                    }
                }
                for (File f : classFile) {
                    String className = f.getPath().substring(f.getPath().indexOf("com"), f.getPath().indexOf(".class")).replace("\\", ".");
                    System.out.println(className);
                    Class<?> clazz = classLoader.loadClass(className);
                    if (clazz.isAnnotationPresent(Component.class)) {
                        Component component = clazz.getAnnotation(Component.class);
                        String beanName = component.value();
                        if ("".equals(beanName)) {
                            beanName = Introspector.decapitalize(clazz.getSimpleName());
                        }
                        BeanDefinition beanDefinition = new BeanDefinition();
                        beanDefinition.setBeanClass(clazz);
                        if (clazz.isAnnotationPresent(Lazy.class)) {
                            beanDefinition.setLazy(clazz.getAnnotation(Lazy.class).value());
                        }
                        if (clazz.isAnnotationPresent(Scope.class)) {
                            beanDefinition.setScope(clazz.getAnnotation(Scope.class).value());
                        }
                        beanDefinitionMap.put(beanName, beanDefinition);
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }


    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new NullPointerException();
        }
        if (beanDefinition.isLazy()) {
            return singletonObjects.get(beanName);
        }
        if ("singleton".equals(beanDefinition.getScope())) {
            Object o = singletonObjects.get(beanName);
            if (o == null) {
                o = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, o);
            }
            return o;
        } else {
            return createBean(beanName, beanDefinition);
        }
    }
}
